// See LICENSE for license details.

//#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <memory.h>
#include <string.h>
#include <math.h>
#include "encoding.h"
#include "mini-printf.h"
#include "diskio.h"
#include "ff.h"
#include "bits.h"
#include "hid.h"
#include "eth.h"
#include "elfriscv.h"
#include "lowrisc_memory_map.h"

FATFS FatFs;   // Work area (file system object) for logical drive

// max size of file image is 16M
#define MAX_FILE_SIZE 0x1000000

// 4K size read burst
#define SD_READ_SIZE 4096

char md5buf[SD_READ_SIZE];

char* clearzero(char* a){
    char* ptr = a;
    while (*ptr != 0 && *ptr == '0' && ptr < a+strlen(a)-1) ptr++;
    return ptr;
}
 
void add(char* ia, char* ib, size_t res_size, char* res){
    
    char* a = ia;
    char* b = ib;
    
    int c = 0, len_a = (int)strlen(a), len_b = (int)strlen(b);
    for (size_t i = 0; i < res_size; i++)
    {
        if (len_a > i && len_b > i)
        {
            int addbit = c + (a[len_a - 1 - i] - '0') + (b[len_b - 1 - i] - '0');
            res[res_size - 1 - i] = '0' + (addbit%10);
            if (addbit > 9) c = 1;
            else c = 0;
        }else if (len_a > i)
        {
            int addbit = c + (a[len_a - 1 - i] - '0');
            res[res_size - 1 - i] = '0' + (addbit%10);
            if (addbit > 9) c = 1;
            else c = 0;
        }else if (len_b > i)
        {
            int addbit = c + (b[len_b - 1 - i] - '0');
            res[res_size - 1 - i] = '0' + (addbit%10);
            if (addbit > 9) c = 1;
            else c = 0;
        }else{
            res[res_size - 1 - i] = '0' + c;
            c = 0;
        }
    }
}

int bigger(char* a, char* b){ // a >= b
    char* ptr_a = clearzero(a);
    char* ptr_b = clearzero(b);
    if (strlen(ptr_a) > strlen(ptr_b)) return 1;
    else if(strlen(ptr_a) < strlen(ptr_b)) return 0;
    else{
        while (*ptr_a && *ptr_b)
        {
            if(*ptr_a > *ptr_b) return 1;
            else if(*ptr_a < *ptr_b) return 0;
            ptr_a++;
            ptr_b++;
        }
        return 1;
    }
}

int bigger_blen(char* a, char* b){ // a >= b
    char* ptr_a = clearzero(a);
    char* ptr_b = clearzero(b);
    //printf("[%s vs %s]", ptr_a, ptr_b);
    if (strlen(ptr_a) < strlen(ptr_b)) return 0;
    if (strlen(ptr_a) > strlen(ptr_b)) return 1;
    for (size_t i = 0; i < strlen(ptr_b)-1; i++)
    {
        if(*ptr_a > *ptr_b) return 1;
        else if(*ptr_a < *ptr_b) return 0;
        else{
            ptr_a++;
            ptr_b++;
        }
    }
    if (*ptr_a >= *ptr_b) return 1;
    else return 0;
}

void sub(char* ia, char* ib, size_t res_size, char* res){
    
    char* a = ia;
    char* b = ib;
    //printf("\n>>>> %s - %s = ",a,b);
    int c = 0, len_a = (int)strlen(a), len_b = (int)strlen(b);
    for (size_t i = 0; i < res_size; i++)
    {
        if (len_a > i && len_b > i)
        {
            if(a[len_a - 1 - i] + c >= b[len_b - 1 - i]){
                res[res_size - 1 - i] = (a[len_a - 1 - i] - '0') + c - (b[len_b - 1 - i] - '0') + '0';
                c = 0;
            }else{
                res[res_size - 1 - i] = (a[len_a - 1 - i] - '0') + c + 10 - (b[len_b - 1 - i] - '0') + '0';
                c = -1;
            }
        }else if (len_a > i)
        {
            if(a[len_a - 1 - i] + c >= '0'){
                res[res_size - 1 - i] = a[len_a - 1 - i] + c;
                c = 0;
            }else{
                res[res_size - 1 - i] = a[len_a - 1 - i] + c + 10;
                c = -1;
            }
        }
    }
    //printf("%s \n",res);
}

void mul_with_mod(char* ia, char* ib, char* imod, char* imul){
    //printf("ia=%lu ib=%lu imod=%lu imul=%lu\n", strlen(ia), strlen(ib), strlen(imod), strlen(imul));
    char res[320];
    size_t res_len = strlen(ia)+strlen(ib)+1;
    //char* res = (char *)malloc(res_len);
    memset(res,'0',res_len*sizeof(char));
    res[res_len] = 0;
    //printf("ia=%s\nib=%s\nimod=%s\nimul=%s\n", ia, ib, imod, imul);
    for (size_t i = 0; i < strlen(ib); i++)
    {
        int c = 0, b_tag = ib[strlen(ib) - 1 - i];
        for (size_t j = 0; j < strlen(ia); j++)
        {
            int num = (res[res_len - 1 - j - i] - '0') + c + (b_tag - '0') * (ia[strlen(ia) - 1 - j] - '0');
            res[res_len - 1 - j - i] = num % 10 + '0';
            c = num / 10;
            //printf("[ProcIn] res=%s [a:%d b:%d]\n", res , (ia[strlen(ia) - 1 - j] - '0'), (b_tag - '0'));
        }
        res[res_len - 1 - strlen(ia) - i] = c + res[res_len - 1 - strlen(ia) - i];
    }
    char* temp_res = res;
    
    if (bigger(res, imod))
    {
        char* ptr = res;
        char temp[200];
        int temp_len = (int)(strlen(imod) + 1);
        memset(temp,'0',temp_len*sizeof(char));
        temp[temp_len] = 0;
        int i_move = 0;
        char* last_ptr = ptr;
        while (bigger(ptr, imod) || bigger(last_ptr, imod)){
            
            temp_res = res;
            while(*temp_res == '0') temp_res++;
            //printf("[ProcIn] res=%s\n", temp_res);
            for (size_t i = 1; i < temp_len; i++) temp[i] = ptr[i-1];
            if(ptr == res) temp[0] = '0';
            else {
                ptr--;
                char* pre_ptr = ptr;
                temp[0] = *pre_ptr;
                ptr++;
            }
            if (bigger_blen(temp,imod)) {
                //printf("%s mod 0%s = ", temp, imod);
                sub(temp, imod, temp_len, temp);
                //printf("%s [%d]\n", temp, i_move);
                for (size_t i = 1; i < temp_len; i++) ptr[i-1] = temp[i];
                if(ptr != res){
                    ptr--;
                    ptr[0] = temp[0];
                    ptr++;
                }
            }else{
                i_move++;
                last_ptr = ptr;
                ptr++;
            }
        }
    }
    //printf("[GetRes] res=%s\n", res);
    char* ptr_res = clearzero(res);
    for (size_t i = 0; i < strlen(ptr_res); i++) imul[strlen(imul) - 1 - i] = ptr_res[strlen(ptr_res) - 1 - i];
}

void en_code(char* ibase, char* iprivate_key, char* imod, char* imul){
    
    char exp_base[200];
    char result[200];
    size_t len = strlen(imod);
    memset(exp_base,'0',len*sizeof(char));
    memset(result,'0',len*sizeof(char));
    result[len-1] = '1';
    result[len] = exp_base[len] = 0;
    for (size_t i = 0; i < strlen(ibase); i++) exp_base[len-1-i] = ibase[strlen(ibase)-1-i];
    //printf("%s[%lu]\n", exp_base, strlen(exp_base));
    
    for (size_t i = 0; i < strlen(iprivate_key); i++)
    {
        char res1[200];
        size_t res_size = strlen(imod);
        if (i > 0)
        {
            for (size_t iter = 0; iter < 4; iter++)
            {
                memset(res1,'0',res_size*sizeof(char));
                res1[res_size] = 0;
                mul_with_mod(exp_base, exp_base, imod, res1);
                for (size_t k = 0; k < strlen(exp_base); k++) exp_base[k] = res1[k];
            }
        }
        int gitnum = 0;
        if (iprivate_key[strlen(iprivate_key)-1-i] >= '0' && iprivate_key[strlen(iprivate_key)-1-i] <= '9') gitnum = iprivate_key[strlen(iprivate_key)-1-i] - '0';
        else if (iprivate_key[strlen(iprivate_key)-1-i] >= 'a' && iprivate_key[strlen(iprivate_key)-1-i] <= 'f') gitnum = iprivate_key[strlen(iprivate_key)-1-i] - 'a' + 10;
        else gitnum = 0;
        for (size_t k = 0; k < gitnum; k++)
        {
            memset(res1,'0',res_size*sizeof(char));
            res1[res_size] = 0;
            mul_with_mod(result, exp_base, imod, res1);
            for (size_t k = 0; k < strlen(exp_base); k++) result[k] = res1[k];
        }
        //printf("[%d]\t%s\n", gitnum, result);
        //printf("%p:%p [exp_base %lu]\t%s [%d]\n", exp_base, res1, i, exp_base, gitnum);
    }
    
    for (size_t i = 0; i < strlen(result); i++) imul[i] = result[i];
}
void de_code(char* ibase, size_t siz, char* ipublic_key, char* imod, char* imul){
    
    char exp_base[200];
    char result[200];
    size_t len = strlen(imod);
    memset(exp_base,'0',len*sizeof(char));
    memset(result,'0',len*sizeof(char));
    result[len-1] = '1';
    result[len] = exp_base[len] = 0;
    for (size_t i = 0; i < siz; i++) exp_base[len-1-i] = ibase[siz-1-i];
    printf("%s\n", exp_base);
    printf("%s\n", imod);
    
    for (size_t i = 0; i < strlen(ipublic_key); i++)
    {
        char res1[200];
        size_t res_size = strlen(imod);
        if (i > 0)
        {
            for (size_t iter = 0; iter < 4; iter++)
            {
                memset(res1,'0',res_size*sizeof(char));
                res1[res_size] = 0;
                mul_with_mod(exp_base, exp_base, imod, res1);
                for (size_t k = 0; k < strlen(exp_base); k++) exp_base[k] = res1[k];
                printf(">>> %s\n", exp_base);
            }
            
        }
        int gitnum = 0;
        if (ipublic_key[strlen(ipublic_key)-1-i] >= '0' && ipublic_key[strlen(ipublic_key)-1-i] <= '9') gitnum = ipublic_key[strlen(ipublic_key)-1-i] - '0';
        else if (ipublic_key[strlen(ipublic_key)-1-i] >= 'a' && ipublic_key[strlen(ipublic_key)-1-i] <= 'f') gitnum = ipublic_key[strlen(ipublic_key)-1-i] - 'a' + 10;
        else gitnum = 0;
        for (size_t k = 0; k < gitnum; k++)
        {
            memset(res1,'0',res_size*sizeof(char));
            res1[res_size] = 0;
            mul_with_mod(result, exp_base, imod, res1);
            for (size_t k = 0; k < strlen(exp_base); k++) result[k] = res1[k];
        }
        printf("[%d]\t%s\n", gitnum, result);
        //printf("%p:%p [exp_base %lu]\t%s [%d]\n", exp_base, res1, i, exp_base, gitnum);
    }
    for (size_t i = 0; i < strlen(result); i++) imul[i] = result[i];
}

int hex_eq_dec(char* hex_str, char* dec_str){
    size_t len = strlen(hex_str) << 2;
    char str_hex[len+1];
    memset(str_hex, '0', (len)*sizeof(char));
    str_hex[len] = 0;
    for (size_t i = 0; i < strlen(hex_str); i++)
    {
        int gitnum = 0;
        if (hex_str[i] >= '0' && hex_str[i] <= '9') gitnum = hex_str[i] - '0';
        else if (hex_str[i] >= 'a' && hex_str[i] <= 'f') gitnum = hex_str[i] - 'a' + 10;
        else gitnum = 0;
        switch (gitnum)
        {
        case  0: str_hex[i<<2] = '0'; str_hex[(i<<2)+1] = '0'; str_hex[(i<<2)+2] = '0'; str_hex[(i<<2)+3] = '0'; break;
        case  1: str_hex[i<<2] = '0'; str_hex[(i<<2)+1] = '0'; str_hex[(i<<2)+2] = '0'; str_hex[(i<<2)+3] = '1'; break;
        case  2: str_hex[i<<2] = '0'; str_hex[(i<<2)+1] = '0'; str_hex[(i<<2)+2] = '1'; str_hex[(i<<2)+3] = '0'; break;
        case  3: str_hex[i<<2] = '0'; str_hex[(i<<2)+1] = '0'; str_hex[(i<<2)+2] = '1'; str_hex[(i<<2)+3] = '1'; break;
        case  4: str_hex[i<<2] = '0'; str_hex[(i<<2)+1] = '1'; str_hex[(i<<2)+2] = '0'; str_hex[(i<<2)+3] = '0'; break;
        case  5: str_hex[i<<2] = '0'; str_hex[(i<<2)+1] = '1'; str_hex[(i<<2)+2] = '0'; str_hex[(i<<2)+3] = '1'; break;
        case  6: str_hex[i<<2] = '0'; str_hex[(i<<2)+1] = '1'; str_hex[(i<<2)+2] = '1'; str_hex[(i<<2)+3] = '0'; break;
        case  7: str_hex[i<<2] = '0'; str_hex[(i<<2)+1] = '1'; str_hex[(i<<2)+2] = '1'; str_hex[(i<<2)+3] = '1'; break;
        case  8: str_hex[i<<2] = '1'; str_hex[(i<<2)+1] = '0'; str_hex[(i<<2)+2] = '0'; str_hex[(i<<2)+3] = '0'; break;
        case  9: str_hex[i<<2] = '1'; str_hex[(i<<2)+1] = '0'; str_hex[(i<<2)+2] = '0'; str_hex[(i<<2)+3] = '1'; break;
        case 10: str_hex[i<<2] = '1'; str_hex[(i<<2)+1] = '0'; str_hex[(i<<2)+2] = '1'; str_hex[(i<<2)+3] = '0'; break;
        case 11: str_hex[i<<2] = '1'; str_hex[(i<<2)+1] = '0'; str_hex[(i<<2)+2] = '1'; str_hex[(i<<2)+3] = '1'; break;
        case 12: str_hex[i<<2] = '1'; str_hex[(i<<2)+1] = '1'; str_hex[(i<<2)+2] = '0'; str_hex[(i<<2)+3] = '0'; break;
        case 13: str_hex[i<<2] = '1'; str_hex[(i<<2)+1] = '1'; str_hex[(i<<2)+2] = '0'; str_hex[(i<<2)+3] = '1'; break;
        case 14: str_hex[i<<2] = '1'; str_hex[(i<<2)+1] = '1'; str_hex[(i<<2)+2] = '1'; str_hex[(i<<2)+3] = '0'; break;
        case 15: str_hex[i<<2] = '1'; str_hex[(i<<2)+1] = '1'; str_hex[(i<<2)+2] = '1'; str_hex[(i<<2)+3] = '1'; break;
        default: str_hex[i<<2] = '0'; str_hex[(i<<2)+1] = '0'; str_hex[(i<<2)+2] = '0'; str_hex[(i<<2)+3] = '0'; break;
        }
    }

    size_t len_dec = strlen(dec_str);
    char str_dec[len_dec+1];
    memset(str_dec, '0', (len_dec)*sizeof(char));
    str_dec[len_dec] = 0;
    for (size_t i = 0; i < strlen(str_dec); i++) str_dec[i] = dec_str[i];
    // printf("%s\n%s\n", str_hex, str_dec);
    int PosCompare = (int)strlen(str_hex) - 1;
    while(1)
    {
        
        int tailnum = str_dec[strlen(str_dec)-1] - '0';
        
        if (tailnum % 2 == 0 && str_hex[PosCompare] == '1') return 0;
        else if(tailnum % 2 == 1 && str_hex[PosCompare] == '0') return 0;
        
        PosCompare -= 1;
        if(PosCompare < 0) break;

        int c = 0;
        for (size_t k = 0; k < strlen(str_dec); k++)
        {
            int tempnum = c * 10 + str_dec[k] - '0';
            str_dec[k] = tempnum / 2 + '0';
            c = tempnum % 2;
        }
        //printf("[Mod 2] %s [%d] <%d>\n", str_dec, tailnum % 2, PosCompare);
    }
    
    return 1;
}

void sd_main(int sw)
{
  FIL fil;                // File object
  FIL fil_md5;
  FRESULT fr;             // FatFs return code
  FRESULT fr_md5;             // FatFs return code
  uint8_t *boot_file_buf = (uint8_t *)(get_ddr_base()) + ((uint64_t)get_ddr_size()) - MAX_FILE_SIZE; // at the end of DDR space
  uint8_t *memory_base = (uint8_t *)(get_ddr_base());

  // Register work area to the default drive
  if(f_mount(&FatFs, "", 1)) {
    printf("Fail to mount SD driver!\n");
    return 1;
  }

  // Open a file
  printf("Load boot.bin into memory\n");
  fr = f_open(&fil, "boot.bin", FA_READ);
  if (fr) {
    printf("Failed to open boot!\n");
    return (int)fr;
  }

  printf("Load md5.txt into memory\n");
  fr_md5 = f_open(&fil_md5, "md5.txt", FA_READ);
  if (fr_md5) {
    printf("Failed to open boot!\n");
    return (int)fr_md5;
  }

  // Read file into memory
  uint8_t *hashbuf;
  uint32_t fsize = 0;           // file size count
  uint32_t br;                  // Read count
  do {
    char *sum;
    fr = f_read(&fil, boot_file_buf+fsize, SD_READ_SIZE, &br);  // Read a chunk of source file
    if (!fr)
      {
        hid_send('\b');
	      hid_send("|/-\\"[(fsize/SD_READ_SIZE)&3]);
	      fsize += br;
      }
  } while(!(fr || br == 0));
  fsize = fil.fsize;

  // Read md5 file into memory
  uint8_t *boot_file_buf_md5 = boot_file_buf + fsize + 16;
  uint32_t fsize_md5 = 0;           // file size count
  uint32_t br_md5;                  // Read count
  do {
    char *sum;
    fr_md5 = f_read(&fil_md5, boot_file_buf_md5+fsize_md5, SD_READ_SIZE, &br_md5);  // Read a chunk of source file
    if (!fr)
      {
        hid_send('\b');
	      hid_send("|/-\\"[(fsize/SD_READ_SIZE)&3]);
	      fsize_md5 += br_md5;
      }
  } while(!(fr_md5 || br_md5 == 0));
  fsize_md5 = fil_md5.fsize;

  // Close the file
  if(f_close(&fil)) {
    printf("fail to close file!");
    return 1;
  }
  if(f_close(&fil_md5)) {
    printf("fail to close file!");
    return 1;
  }
  if(f_mount(NULL, "", 1)) {         // unmount it
    printf("fail to umount disk!");
    return 1;
  }

  printf("[Boot.bin ] Loaded %d bytes to memory address %x from SD card of %d bytes.\n", fsize, boot_file_buf, fsize);
  printf("[MD5.txt  ] Loaded %d bytes to memory address %x from SD card of %d bytes.\n", fsize_md5, boot_file_buf_md5, fsize_md5);
//#if 1

  char* modulus = "10814053356812995165255334783554438541675060597292242418761617732402803534577669927555980915400220136035543187252538844080709307557734193733230472435342247";
  char* public_hex = "10001";

  size_t res_size = strlen(modulus);
  char decode_md5[200];
  memset(decode_md5,'0',res_size*sizeof(char));
  decode_md5[res_size] = 0;

  printf("encoded md5 = ");
  for (size_t i = 0; i < res_size; i++)
  {
    printf("%c", boot_file_buf_md5[i]);
  }
  printf("\n-----------------\n");
  hashbuf = hash_buf(boot_file_buf, fsize);
  printf("hash = %s\n", hashbuf);
  
  /* -------------------------Should be deleted after debugging----------------------- */
  uint8_t * tagetbuf = "cc42d62cd5557313d6bee7312d5b667b";
  printf("tagetbuf = %s\n", tagetbuf);
  /* -------------------------Should be deleted after debugging----------------------- */

  de_code(boot_file_buf_md5, res_size, public_hex, modulus, decode_md5);
  printf("decoded md5 = ");
  for (size_t i = 0; i < res_size; i++)
  {
    printf("%c", decode_md5[i]);
  }
  printf("\n-----------------\n");
  
  int eq = hex_eq_dec(hashbuf, clearzero(decode_md5));
  if (eq) printf("Verified Pass!\n");
  else printf("Error in Verify process!\n");

  if (eq)
  {
    printf("------------------------Elf Verified!---------------------------\n");

    printf("load elf to DDR memory\n");
    if(br = load_elf(boot_file_buf, fsize))
      printf("elf read failed with code %d", br);
    printf("Boot the loaded program...\n");
    // read elf
    uintptr_t mstatus = read_csr(mstatus);
    mstatus = INSERT_FIELD(mstatus, MSTATUS_MPP, PRV_M);
    mstatus = INSERT_FIELD(mstatus, MSTATUS_MPIE, 1);
    write_csr(mstatus, mstatus);
    write_csr(mepc, memory_base);
    asm volatile ("mret");
    
  }else{
    printf("-------------Loading process is banned for security.-------------\n");
  }
}

int just_jump (void)
{
  uint8_t *memory_base = (uint8_t *)(get_ddr_base());
  uintptr_t mstatus = read_csr(mstatus);
  mstatus = INSERT_FIELD(mstatus, MSTATUS_MPP, PRV_M);
  mstatus = INSERT_FIELD(mstatus, MSTATUS_MPIE, 1);
  write_csr(mstatus, mstatus);
  write_csr(mepc, memory_base);
  asm volatile ("mret");
}

#define HELLO "Hello LowRISC! "__TIMESTAMP__": "

int main (void)
{
  int i, sw = sd_resp(31);

  loopback_test(8, (sw & 0xF) == 0xF);
  
  printf("lowRISC boot program (Modified by Hongyi-Li)\n=====================================\n");

  for (i = 10000000; i; i--)
    write_led(i);

  if (sw & 1)
      {
        printf(HELLO"Jumping to DRAM because SW0 is high ..\n");
        just_jump();
      }
  else if (sw & 2)
      {
        printf(HELLO"Booting from FLASH because SW1 is high ..\n");
        sd_main(sw>>3);
      }
  else if (sw & 4)
      {
        printf(HELLO"Booting from Ethernet because SW2 is high ..\n");
        eth_main();
      }
  else printf(HELLO"Turn on SW0 for gdb loading, SW1 for SD-card loading, or SW2 for Ethernet loading\n");
  
  printf(HELLO"\n----------------------End----------------------\n");

  for (i = 1; i; i++) write_led(i>>16);
}

int lowrisc_init(unsigned long addr, int ch, unsigned long quirks);
void tohost_exit(long code);

unsigned long get_tbclk (void)
{
	unsigned long long tmp = 1000000;
	return tmp;
}

char *env_get(const char *name)
{
  return (char *)0;
}

void *malloc(size_t len)
{
  static unsigned long rused = 0;
  char *rd = rused + (char *)(ddr_base_addr+0x6800000);
  rused += ((len-1)|7)+1;
  return rd;
}

void *calloc(size_t nmemb, size_t size)
{
  size_t siz = nmemb*size;
  char *ptr = malloc(siz);
  if (ptr)
    {
      memset(ptr, 0, siz);
      return ptr;
    }
  else
    return (void*)0;
}

void free(void *ptr)
{

}

int init_mmc_standalone(int sd_base_addr);

DSTATUS disk_initialize (uint8_t pdrv)
{
  printf("\nu-boot based first stage boot loader\n");
  init_mmc_standalone(sd_base_addr);
  return 0;
}

int ctrlc(void)
{
	return 0;
}

void *find_cmd_tbl(const char *cmd, void *table, int table_len)
{
  return (void *)0;
}

unsigned long timer_read_counter(void)
{
  return read_csr(0xb00) / 10;
}

void __assert_fail (const char *__assertion, const char *__file,
                           unsigned int __line, const char *__function)
{
  printf("assertion %s failed, file %s, line %d, function %s\n", __assertion, __file,  __line, __function);
  tohost_exit(1);
}

void *memalign(size_t alignment, size_t size)
{
  char *ptr = malloc(size+alignment);
  return (void*)((-alignment) & (size_t)(ptr+alignment));
}

int do_load(void *cmdtp, int flag, int argc, char * const argv[], int fstype)
{
  return 1;
}

int do_ls(void *cmdtp, int flag, int argc, char * const argv[], int fstype)
{
  return 1;
}

int do_size(void *cmdtp, int flag, int argc, char * const argv[], int fstype)
{
                return 1;
}

DRESULT disk_read (uint8_t pdrv, uint8_t *buff, uint32_t sector, uint32_t count)
{
  while (count--)
    {
      read_block(buff, sector++);
      buff += 512;
    }
  return FR_OK;
}

DRESULT disk_write (uint8_t pdrv, const uint8_t *buff, uint32_t sector, uint32_t count)
{
  return FR_INT_ERR;
}

DRESULT disk_ioctl (uint8_t pdrv, uint8_t cmd, void *buff)
{
  return FR_INT_ERR;
}

DSTATUS disk_status (uint8_t pdrv)
{
  return FR_INT_ERR;
}

void part_init(void *bdesc)
{

}

void part_print(void *desc)
{

}

void dev_print(void *bdesc)
{

}

unsigned long mmc_berase(void *dev, int start, int blkcnt)
{
        return 0;
}

unsigned long mmc_bwrite(void *dev, int start, int blkcnt, const void *src)
{
        return 0;
}

const char version_string[] = "LowRISC minimised u-boot for SD-Card";
